package org.liang.transfer.sftp;

import java.util.Properties;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.liang.transfer.Transfer;
import org.liang.transfer.exception.UnconnectedException;
import org.liang.transfer.exception.UncorrectedRemotePathException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

/**
 * SFTP 客户端.
 * 
 * <pre>
 * 如果未指定端口，则使用默认端口（22）；
 * </pre>
 * 
 * @author L.Yang
 * @version v1.0 2016年4月7日 下午4:16:49
 */
public class SFTP implements Transfer {
    private static final Logger LOGGER = LoggerFactory.getLogger(SFTP.class);
    private static final int DEF_PORT = 22;

    private boolean isconnected;
    private Session session;
    private Channel channel;
    public ChannelSftp sftp;
    
    @Override
    public void connect(String host, String username, String password) {
        connect(host, DEF_PORT, username, password);
    }

    @Override
    public void connect(String host, int port, String username, String password) {
        try {
            session = new JSch().getSession(username, host, port);
            LOGGER.trace("Session created: {}.", session);
            session.setPassword(password);

            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            session.setConfig(sshConfig);
            session.setTimeout(60 * 1000);
            session.connect();
            LOGGER.trace("Session connected.");

            LOGGER.trace("Opening Channel.");
            channel = session.openChannel("sftp");
            channel.connect();
            LOGGER.debug("Connected successfully to {}:{}, username:{}, channel:{} ",
                            new Object[] {host, port, username, channel});
            this.sftp = (ChannelSftp) channel;
            isconnected = true;
        } catch (JSchException e) {
            LOGGER.error("SFTP 连接失败 :{}", ExceptionUtils.getStackTrace(e));
        }
    }

    /*
     * 上传文件到服务器.
     * 
     * <pre>
     * 如果是目录则进行递归上传，将目录下所有文件夹、文件上传到服务器。
     * </pre>
     * 
     * @param remote
     *            远程FTP服务器文件路径
     * @param local
     *            本地存储文件的完整路径（包含文件名）
     */
    @Override
    public void upload(String remote, String local) {
        try {
            if (!isconnected) throw new UnconnectedException();
            SFTPUploader uploader = new SFTPUploader(sftp);
            uploader.upload(remote, local);
        } catch (UnconnectedException e) {
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
            throw e;
        } catch (Exception e) {
            disconnect();
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
        }
    }

    /*
     * 下载远程服务器上的文件，并存储到本地.
     * 
     * <pre>
     * remote 如果是文件夹则将对文件夹中的文件进行filter过滤，将符合filter的文件下载；
     * 如果local 是文件名则使用locl 如果local 是路径则使用服务器文件名；
     * 过滤规则： 远程文件命中包含filter字符串。
     * </pre>
     * 
     * @param remote
     *            远程FTP服务器文件的完整路径
     * @param local
     *            本地存储文件的完整路径
     * @param filter
     *            过滤内容
     */
    @Override
    public void download(String remote, String local, String filter) {
        try {
            if (!isconnected) throw new UnconnectedException();
            SFTPDownloader downloader = new SFTPDownloader(sftp);
            downloader.download(remote, local, filter);
        } catch (UncorrectedRemotePathException e) {
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
            disconnect();
            throw e;
        } catch (UnconnectedException e) {
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
            throw e;
        } catch (Exception e) {
            disconnect();
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
        }
    }

    /*
     * 下载远程服务器上的文件，并存储到本地.
     * 
     * <pre>
     * remote 如果是文件夹则将下载文件夹下的所有文件
     * 如果local 是文件名则使用locl 如果local 是路径则使用服务器文件名；
     * </pre>
     * 
     * @param remote
     *            远程FTP服务器文件的完整路径
     * @param local
     *            本地存储文件的完整路径
     */
    @Override
    public void download(String remote, String local) {
        try {
            if (!isconnected) throw new UnconnectedException();
            SFTPDownloader downloader = new SFTPDownloader(sftp);
            downloader.download(remote, local, null);
        } catch (UncorrectedRemotePathException e) {
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
            disconnect();
            throw e;
        } catch (UnconnectedException e) {
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
            throw e;
        } catch (Exception e) {
            disconnect();
            LOGGER.error("SFTP 下载失败 :{}", ExceptionUtils.getStackTrace(e));
        }
    }

    /**
     * 断开SFTP连接.
     */
    public void disconnect() {
        if (channel != null) {
            channel.disconnect();
        }
        if (session != null) {
            session.disconnect();
        }
        if (sftp != null) {
            sftp.quit();
        }
        LOGGER.trace("SFTP 已断开连接");
    }
}
